package com.ht.api.bigtask;

import java.io.PrintWriter;
import java.io.StringWriter;

import com.ht.api.db.Db;
import com.ht.api.db.DbConfig;
import com.ht.api.db.Entity;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 大任务基类<br>
 * 大任务需要维护：执行进度/执行状态/失败恢复/防止重复执行等，因此需要持久化到数据库，以便失败恢复
 * @author asq
 * @createTime 2025年1月15日 09:01:43
 */
@Slf4j
@Data
public abstract class BigTask {
	/** 状态：准备中 */
	protected static final int INIT = 0;
	/** 状态：执行中 */
	protected static final int RUNING = 1;
	/** 状态：执行失败 */
	protected static final int FAIL = 2;
	/** 状态：执行成功 */
	public static final int SUCCESS = 3;

	/** 第一个任务 */
    private transient BigTask firstTask = this;
	/** 下一个任务 */
    private BigTask nextTask;
    /** 任务进度相关信息（每个具体任务节点可以自己存自己的数据） */
    private JSONObject info = JSONUtil.createObj();
    /** 本任务是否已完成 */
    private boolean over;
    /** 总任务数 */
    private int totalTasks = 1; 
    /** 已完成任务数 */
    private int completedTasks; 
    
    /**
     * 设置下一个任务
     * @param task
     */
    public void setNextTask(BigTask task) {
    	this.nextTask = task;
    	this.nextTask.firstTask = this.firstTask;
    }
    
    /**
     * 获取可序列化的数据
     */
    public JSONObject toSerializableData() {
        JSONObject data = JSONUtil.createObj()
            .set("info", this.info)
            .set("over", this.over);
            
        // 如果需要序列化 nextTask，确保也使用 toSerializableData
        if (nextTask != null) {
            data.set("nextTask", nextTask.toSerializableData());
        }
        
        return data;
    }
    
	/**
     * 开始执行任务
     * @param bean 数据库实体
     */
    public void execute(Entity bean) {
    	// 记录任务开始时间，以便计算耗时
    	bean.set("time", System.currentTimeMillis());
    	
    	// 创建一个参数上下文对象，然后开始任务
    	execute(JSONUtil.createObj(), bean);
    }
    
    /**
     * 执行具体任务
     * @param appId 应用ID
     * @param context 参数上下文
     * @param bean 数据库实体
     */
    protected abstract void doExecute(String appId, JSONObject context, Entity bean);
    
    /**
     * 保存任务进度信息
     * @param key
     * @param value
     */
    protected void saveInfo(String key, Object value, Entity bean) {
    	// 保存数据
    	info.set(key, value);
    	
    	// 及时存数据库
    	updateTaskEntity(bean);
    }
    
    /**
     * 开始执行任务
     * @param context 存储参数的上下文
     * @param bean 数据库实体
     */
    private void execute(JSONObject context, Entity bean) {
    	// 执行本任务
    	try {
    		doExecute(bean.getStr("appid"), context, bean);
    		
    		// 标记本任务为“已完成”，如果后续任务失败重新执行时，本任务将不会被重复执行
    		this.over = true;
    		
    		// 本任务的已完成数=总数
    		this.completedTasks = this.totalTasks;
    		
    		// 更新数据库
    		updateTaskEntity(bean, RUNING);
    	} catch (Exception e) {
        	updateTaskEntity(bean, FAIL, e);
        	log.info("[BigTask]执行任务发生异常：{}", e.getMessage(), e);
        	return ;
    	}
    	
    	// 执行下一个任务
    	if (nextTask != null) {
    		nextTask.execute(context, bean);
    	} else {
	    	// 全部任务执行完后，标记为成功
	    	updateTaskEntity(bean, SUCCESS);
	    	log.info("[BigTask]恭喜，全部任务执行成功~");
    	}
    }
    
    /**
     * 获取全部任务的总任务数
     * @return
     */
    private int getSumTotalTasks() {
    	int num = totalTasks;
    	if (nextTask != null) {
    		num += nextTask.getSumTotalTasks();
    	}
    	return num;
    }
    
    /**
     * 获取全部任务的已完成任务数
     * @return
     */
    private int getSumCompletedTasks() {
    	int num = completedTasks;
    	if (nextTask != null) {
    		num += nextTask.getSumCompletedTasks();
    	}
    	return num;
    }
    
    /**
     * 计算完成百分比
     * @return
     */
    private int getProgress() {
    	int sumTotal = getSumTotalTasks();
    	int sumCompleted = getSumCompletedTasks();
    	return sumCompleted * 100 / sumTotal;
    }
    
    /**
     * 更新任务数据
     * @param bean
     */
    private void updateTaskEntity(Entity bean) {
    	updateTaskEntity(bean, null, null);
    }
    
    /**
     * 更新任务数据到数据库
     * @param bean 任务Entity
     * @param status 任务状态
     */
    private void updateTaskEntity(Entity bean, Integer status) {
    	updateTaskEntity(bean, status, null);
    }
    
    /**
     * 更新任务到数据库
     * @param bean
     * @param status
     * @param e
     */
    private void updateTaskEntity(Entity bean, Integer status, Throwable e) {
		// 更新任务状态
    	if (status != null) {
    		bean.set("status", status);
    		
    		if (status == RUNING) {
    			bean.set("msg", "执行中");
    		} else if (status == FAIL) {
    			// 将异常堆栈信息转换为字符串
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                e.printStackTrace(pw);
                String stackTrace = sw.toString();
                bean.set("msg", "发生异常：" + stackTrace);
    		} else if (status == SUCCESS) {
    			bean.set("msg", "执行成功");
    		}
    	}
    	
		// 更新任务耗时
    	long oldCostTime = bean.getLong("cost_time");
    	long costTime = System.currentTimeMillis() - bean.getLong("time");
    	bean.set("cost_time", oldCostTime + costTime);
    	
    	// 更新任务信息
        bean.set("data", JSONUtil.toJsonStr(firstTask));
    	
    	// 重新计时
    	bean.set("time", System.currentTimeMillis());
    	
    	// 计算完成百分比
    	bean.set("progress", firstTask.getProgress());
    	
    	// 保存
    	Db.insert(DbConfig.useSysDb(), bean);
    }
}
